package zh.dummy.netty;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import zh.dummy.netty.server.handler.DefaultChannelHandler;
import zh.dummy.netty.server.handler.DefaultServerChannelHandler;

public class NettyServer {
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	protected final int defaultPort = 9100;
	
	protected final String host = "127.0.0.1";
	
	private volatile static NettyServer nettyServer;
	
	private volatile static boolean runEnable = true;
	
	private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
			
	protected int port;
	
	public void bind(int port){
		this.port = port;
	}
	
	private NettyServer(){
		
	}
	
	public static NettyServer getInstance(){
		if(nettyServer == null){
			synchronized (NettyServer.class) {
				nettyServer = new NettyServer();
			}
		}
		return nettyServer;
	}
	
	protected void on(){
		ensurePort();
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try{
			
			ChannelHandler channelHandler = new DefaultChannelHandler();
			ChannelHandler serverChannelHandler = new DefaultServerChannelHandler();
			
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class)
			.option(ChannelOption.SO_BACKLOG, 1024)
			.handler(serverChannelHandler)
			.childHandler(channelHandler);
			ChannelFuture f = b.bind(host,port).sync();
			if(logger.isInfoEnabled()){
				logger.info("netty server start in host:{} , port:{}",host,port);
			}
			f.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
			if(runEnable){
				if(logger.isInfoEnabled()){
					logger.info("try to restart server");
				}
				executor.schedule(new Runnable(){
					@Override
					public void run() {
						on();
					}
				}, 5, TimeUnit.SECONDS);
			}else{
				executor.shutdown();
			}
		}
	}

	protected void ensurePort() {
		if(this.port == 0){
			this.port = defaultPort;
		}
	}
	
	public void start(){
		executor.execute(new Runnable(){
			@Override
			public void run() {
				on();
			}
		});
	}
}
